#include "../include/AsynchronousFinder.h"

#include <map>
#include <vector>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <aio.h>
#include <fcntl.h>
#include <aio.h>
#include <signal.h>
#include <errno.h>
#include <iostream>
#include <sys/stat.h>
#include <cstdlib>
#include <unistd.h>

#define BUF_SIZE 20     /* Size of buffers for read operations */

#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

#define errMsg(msg)  do { perror(msg); } while (0)

struct ioRequest {      /* Application-defined structure for tracking
                          I/O requests */
   int           reqNum;
   int           status;
   struct aiocb *aiocbp;
};

static volatile sig_atomic_t gotSIGQUIT = 0;
                       /* On delivery of SIGQUIT, we attempt to
                          cancel all outstanding I/O requests */

static void             /* Handler for SIGQUIT */
quitHandler(int sig)
{
   gotSIGQUIT = 1;
}

#define IO_SIGNAL SIGUSR1   /* Signal used to notify I/O completion */

static void                 /* Handler for I/O completion signal */
aioSigHandler(int sig, siginfo_t *si, void *ucontext)
{
 //  write(STDOUT_FILENO, "I/O completion signal received\n", 31);

   /* The corresponding ioRequest structure would be available as
          struct ioRequest *ioReq = si->si_value.sival_ptr;
      and the file descriptor would then be available via
          ioReq->aiocbp->aio_fildes */
}

void AsynchronousFinder::setFiles(std::vector<std::string> _files) {
    m_files = _files;
}

void AsynchronousFinder::setParameters(std::map<const char*, bool> _params) {
    m_parameters = _params;
}

void AsynchronousFinder::setPattern(std::string _pattern) {
    m_pattern = _pattern;
}

void AsynchronousFinder::find() {
    struct ioRequest* ioList = this->openFiles();

    const size_t    bufSize = 50;
    char            buf[bufSize];
    size_t          fileNo = 0;
    unsigned        lineNo = 1;
    off_t           offset = 50;
    std::string*    bf;
    std::string     line;
    size_t          found;
    int end = lseek(ioList[fileNo].aiocbp->aio_fildes, 0L, SEEK_END);

while (fileNo < m_files.size()) {
    std::cout << "file: " << this->m_files[fileNo] << std::endl;

    lseek(ioList[fileNo].aiocbp->aio_fildes, 0L, SEEK_SET);
    do {
        read(ioList[fileNo].aiocbp->aio_fildes, buf, bufSize);
        lseek(ioList[fileNo].aiocbp->aio_fildes, offset, SEEK_SET);
        bf = new std::string(buf);
        found = bf->find(this->m_pattern);
        if (found != std::string::npos)
            std::cout << lineNo << ": " << *bf << std::endl << std::endl;
        ++lineNo;
        offset += 50;
    }
     while (offset < end);
    ++fileNo;
}
    delete bf;
    delete ioList;
}

struct ioRequest* AsynchronousFinder::openFiles() {
   struct ioRequest *ioList;
   struct aiocb *aiocbList;
   struct sigaction sa;
   int s, j;
   int openReqs;       /* Number of I/O requests still in progress */
    int numReqs = 0;

   numReqs = m_files.size();

    ioList = new ioRequest[numReqs];
    aiocbList = new aiocb[numReqs];

   /* Establish handlers for SIGQUIT and the I/O completion signal */

   sa.sa_flags = SA_RESTART;
   sigemptyset(&sa.sa_mask);

   sa.sa_handler = quitHandler;
   if (sigaction(SIGQUIT, &sa, NULL) == -1)
       errExit("sigaction");

   sa.sa_flags = SA_RESTART | SA_SIGINFO;
   sa.sa_sigaction = aioSigHandler;
   if (sigaction(IO_SIGNAL, &sa, NULL) == -1)
       errExit("sigaction");

   /* Open each file specified on the command line, and queue
      a read request on the resulting file descriptor */
  //  std::cout << "aio requests, numReqs " << numReqs << std::endl;
   for (j = 0; j < numReqs; j++) {
     //  std::cout << "req: " << j << std::endl;
       ioList[j].reqNum = j;
       ioList[j].status = EINPROGRESS;
       ioList[j].aiocbp = &aiocbList[j];

       ioList[j].aiocbp->aio_fildes = open(m_files[j].c_str(), O_RDONLY);
       if (ioList[j].aiocbp->aio_fildes == -1)
           errExit("open");

       ioList[j].aiocbp->aio_buf = malloc(BUF_SIZE);
       if (ioList[j].aiocbp->aio_buf == NULL)
           errExit("malloc");

       ioList[j].aiocbp->aio_nbytes = BUF_SIZE;
       ioList[j].aiocbp->aio_reqprio = 0;
       ioList[j].aiocbp->aio_offset = 0;
       ioList[j].aiocbp->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
       ioList[j].aiocbp->aio_sigevent.sigev_signo = IO_SIGNAL;
       ioList[j].aiocbp->aio_sigevent.sigev_value.sival_ptr =
                               &ioList[j];

       s = aio_read(ioList[j].aiocbp);
       if (s == -1)
           errExit("aio_read");
   }

   openReqs = numReqs;

   /* Loop, monitoring status of I/O requests */

   while (openReqs > 0) {
    //   sleep(3);       /* Delay between each monitoring step */

       if (gotSIGQUIT) {

           /* On receipt of SIGQUIT, attempt to cancel each of the
              outstanding I/O requests, and display status returned
              from the cancellation requests */

           printf("got SIGQUIT; canceling I/O requests: \n");

           for (j = 0; j < numReqs; j++) {
               if (ioList[j].status == EINPROGRESS) {
             //      printf("    Request %d on descriptor %d:", j,
//                           ioList[j].aiocbp->aio_fildes);
                   s = aio_cancel(ioList[j].aiocbp->aio_fildes,
                           ioList[j].aiocbp);
                   if (s == AIO_CANCELED)
                       printf("I/O canceled\n");
                   else if (s == AIO_NOTCANCELED)
                           printf("I/O not canceled\n");
                   else if (s == AIO_ALLDONE)
                       printf("I/O all done\n");
                   else
                       errMsg("aio_cancel");
               }
           }

           gotSIGQUIT = 0;
       }

       /* Check the status of each I/O request that is still
          in progress */

       for (j = 0; j < numReqs; j++) {
           if (ioList[j].status == EINPROGRESS) {
        //       printf("    for request %d (descriptor %d): ",
             //         j, ioList[j].aiocbp->aio_fildes);
               ioList[j].status = aio_error(ioList[j].aiocbp);

               switch (ioList[j].status) {
               case 0:
              //     printf("I/O succeeded\n");
                   break;
               case EINPROGRESS:
              //     printf("In progress\n");
                   break;
               case ECANCELED:
              //     printf("Canceled\n");
                   break;
               default:
               //    errMsg("aio_error");
                   break;
               }

               if (ioList[j].status != EINPROGRESS)
                   openReqs--;
           }
       }
   }

    printf("All I/O requests completed\n");

    /* Check status return of all I/O requests */

    printf("aio_return():\n");
    for (j = 0; j < numReqs; j++) {
       ssize_t s;

       s = aio_return(ioList[j].aiocbp);
    }

//    delete[] aiocbList;
    return ioList;
}
